/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package biofilter.filters;

import biofilter.exceptions.InvalidRecordException;
import biofilter.records.GFFRecord;
import biofilter.records.Record;
import biofilter.util.SequencePosition;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;

/**
 * The FillGap filter takes as input a set of mapped locations (GFFRecords) over a replicon
 * These mapped positions are ordered and then additional regions are created
 * such that no location over the length of the replicon is uncovered with features
 * 
 * e.g. 
 * Given
 * ======   =======    ======    ======
 * Add:
 *       ===       ====      ====
 *
 * @author Daniel Quest
 * @date   April 15 2010
 */
public class FillGapFilter extends JoinSplitFilterBase {

    public FillGapFilter(){

    }
    private String gapToken = "GAP"; //The gap token is the GFF name given to the regions filled.
    public FillGapFilter(String gapToken){
        this.gapToken = gapToken;
    }

    public String getGapToken() {
        return gapToken;
    }

    public void setGapToken(String gapToken) {
        this.gapToken = gapToken;
    }


    private GFFRecord bridgeGap(GFFRecord a, GFFRecord b){
        GFFRecord gap = new GFFRecord();
        gap.setSeqid(a.getSeqid());
        gap.setSource(a.getSource());
        gap.setType(gapToken);
        //start and end
        SequencePosition sp1 =  a.castGFFtoPosition();
        SequencePosition sp2 =  b.castGFFtoPosition();
        if ( sp1.overlaps(sp2) ){
            return null; //overlapping positions do not have a gap.
        }
        // <=====a=====>    <=====b=====>
        if ( a.getEnd() < b.getStart() ){
            gap.setStart( a.getEnd() + 1);
            gap.setEnd( b.getStart() -1);
        }else {
        // <=====b=====>    <=====a=====>
            gap.setStart( b.getEnd() + 1);
            gap.setEnd(  a.getStart() -1);         
        }
        gap.setScore(0.0);
        gap.setStrand('.');
        gap.setPhase(".");
        gap.setAttributes(".");
        return gap;
    }

    @Override
    protected Record transformRecord(Record r) throws InvalidRecordException {
        LinkedList<GFFRecord> anno = new LinkedList<GFFRecord>();
        Iterator i = this.inputQueue.iterator();
        while(i.hasNext()){
             anno.add((GFFRecord) i.next());
        }
        //Everything may be sorted... but do this just to be safe
        Collections.sort(anno,Collections.reverseOrder());
        //Collections.sort(anno); ~~ or the other order if we want
        GFFRecord prev = null;
        GFFRecord cur = null;
        i = anno.iterator();
        while(i.hasNext()){
            prev = cur;
            cur = (GFFRecord) i.next();
            //add the known feature to the output queue
            this.outputQueue.addFirst(cur);
            //check to see if we can also add a gap to the output queue
            if(prev != null && cur != null){
                GFFRecord newGap = bridgeGap(prev, cur);
                if(newGap != null) this.outputQueue.add(1, newGap);
            }
        }
        return (Record) this.outputQueue.removeFirst();
    }



    public Record peek() throws InvalidRecordException {
        throw new UnsupportedOperationException("Not supported yet.");
    }

}
